home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / sound / soundconvert / soundconvert.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  21.2 KB  |  878 lines

  1. /*
  2.     File:        SoundConvert.c
  3.  
  4.     Contains:    Demonstration of sound format conversion support in Sound Manager 3.2
  5.  
  6.     Written by: Andrew Wulf    
  7.  
  8.     Copyright:    Copyright © 1996-1999 by Apple Computer, Inc., All Rights Reserved.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.                 8/2/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  20.                 
  21.  
  22. */
  23. #include <FixMath.h>
  24. #include <Quickdraw.h>
  25. #include <Windows.h>
  26. #include <Dialogs.h>
  27. #include <Files.h>
  28. #include <StandardFile.h>
  29. #include <Packages.h>
  30. #include <OSEvents.h>
  31. #include <Memory.h>
  32. #include <OSUtils.h>
  33. #include <ToolUtils.h>
  34. #include <Types.h>
  35. #include <TextUtils.h>
  36. #include <SegLoad.h>
  37. #include <Gestalt.h>
  38. #include <AIFF.h>
  39. #include <stdio.h>
  40. #include <strings.h>
  41. #include <string.h>
  42. // the following are from sound manager 3.2, not yet in ETO 19
  43. #include <Sound.h>
  44. #include <SoundComponents.h>
  45. #include <SoundInput.h>
  46.  
  47. #define FormatPopupMenu 128
  48. #define RatePopupMenu 129
  49. #define DialogID 129
  50.  
  51. enum {
  52.     Button_Quit = 1,
  53.     Button_Input = 3,
  54.     Button_Convert,
  55.     Static_Filename,
  56.     Radio_8bit,
  57.     Radio_16bit,
  58.     Radio_Mono,
  59.     Radio_Stereo,
  60.     Popup_Format,
  61.     Popup_Rate,
  62.     Static_Notes = 13
  63.     };
  64.  
  65. typedef struct
  66.     {
  67.     FSSpec file;
  68.     SoundComponentData info;
  69.     unsigned long numFrames;
  70.     unsigned long dataOffset;
  71.     Ptr buffer;
  72.     } SoundFileInfo;
  73.  
  74. typedef struct
  75.     {
  76.     FSSpec file;
  77.     SoundComponentData info;
  78.     unsigned long inputBufferFrames;
  79.     unsigned long inputBufferBytes;
  80.     unsigned long outputBytes;
  81.     Ptr buffer;
  82.     } SoundConvertInfo;    
  83.  
  84. typedef struct
  85.     {
  86.     OSType formatType;
  87.     Boolean isCompressor;
  88.     Boolean isDecompressor;
  89.     short dummy;
  90.     } MenuToFormatType, *MenuToFormatTypePtr, **MenuToFormatTypeHandle;
  91.  
  92. MenuToFormatTypeHandle MenuToFormatTypeList = 0;    // used to map menu to format type
  93. short Group1;
  94. short Group2;
  95. Point Where;
  96. short Modifiers;
  97. SoundFileInfo MyFileInfo = { 0, 0, "\p" };
  98. long BuffersSize= 0;
  99.  
  100. #define LAST_SAMPLE_MENU 5
  101. Fixed MenuToSampleRates[LAST_SAMPLE_MENU]
  102.     = { rate44khz, rate22050hz, rate22khz, rate11khz, rate11025hz };
  103.  
  104. #if ((!defined(__MWERKS__))&&(!defined(THINK_C)))
  105.     QDGlobals qd;
  106. #endif
  107.  
  108. OSErr ShowErr(char*text, short err);
  109. void ShowErrAndQuit(char*text, short err);
  110. void BuildFormatMenu(void);
  111. void SetRadio(DialogPtr dlog, short item, short val);
  112. short GetRadio(DialogPtr dlog, short item);
  113. pascal Boolean MyFilter( DialogPtr, EventRecord *, short *);
  114. DialogPtr OpenDialog(void);
  115. void RunDialog(DialogPtr dialog);
  116. void DoDialogUpdate( DialogPtr dialog);
  117. void ShowAControl( DialogPtr dlog, short item );
  118. Boolean GetInputFile(void);
  119. void DoConversion( DialogPtr dialog );
  120. void ShowInfo( DialogPtr dialog );
  121. void SetStaticText( DialogPtr dlog, short item, Byte* s );
  122. void MakeInfoString( SoundFileInfo*info, Byte* s );
  123. void GetWantedData(DialogPtr dialog, SoundComponentData* info);
  124. short GetAControlValue(DialogPtr dlog, short item);
  125. OSErr ConvertSound( SoundFileInfo*, SoundConvertInfo*, SoundConverter sc );
  126. void HiliteAControl( DialogPtr dlog, short item, Boolean state );
  127. OSErr ConvertBufferInMemory( SoundFileInfo*input, short inputFile,
  128.                     SoundConvertInfo*output, short outputFile, SoundConverter sc );
  129. OSErr ConvertBufferInChunks( SoundFileInfo*input, short inputFile,
  130.                     SoundConvertInfo*output, short outputFile, SoundConverter sc );
  131.  
  132. // ----------------------------------------------------------------------------------
  133.  
  134. void main()
  135.     {
  136.     NumVersion version;
  137.     DialogPtr dialog;
  138.  
  139.     MaxApplZone();
  140.  
  141.     InitGraf(&qd.thePort);
  142.     FlushEvents(everyEvent, 0);
  143.     InitWindows();
  144.     InitDialogs(0);
  145.     InitCursor();
  146.     
  147.     version = SndSoundManagerVersion();
  148.     if ( ! ( version.majorRev >= 3 && version.minorAndBugRev >= 0x20 ) )
  149.         ShowErrAndQuit( "Sound Manager is older than 3.2", 0 );
  150.     
  151.     dialog = OpenDialog();
  152.     if ( dialog )
  153.         RunDialog(dialog);
  154.     }
  155.  
  156. // Create our one and only dialog
  157.  
  158. DialogPtr OpenDialog()
  159.     {
  160.     DialogPtr dialog;
  161.  
  162.     InsertMenu(GetMenu(FormatPopupMenu),-1);
  163.     InsertMenu(GetMenu(RatePopupMenu),-1);
  164.     BuildFormatMenu();
  165.     
  166.     dialog = GetNewDialog( DialogID, 0, (WindowPtr)(-1L) );
  167.     SetPort(dialog);
  168.  
  169.     SetRadio( dialog, Group1 = Radio_8bit, 1);
  170.     SetRadio( dialog, Radio_16bit, 0);
  171.     SetRadio( dialog, Group2 = Radio_Mono, 1);
  172.     SetRadio( dialog, Radio_Stereo, 0);
  173.     
  174.     ShowAControl( dialog, Popup_Format );
  175.     ShowAControl( dialog, Popup_Rate );
  176.     
  177.     HiliteAControl( dialog, Button_Convert, false );
  178.  
  179.     ShowWindow(dialog);
  180.     
  181.     return dialog;
  182.     }
  183.  
  184. // Execute dialog until quit button hit
  185.  
  186. void RunDialog( DialogPtr dialog )
  187.     {
  188.     Boolean keepGoing = true;
  189.     short itemHit;
  190.     ModalFilterUPP filter = NewModalFilterProc( MyFilter );
  191.     
  192.     while (keepGoing)
  193.         {
  194.         ModalDialog( filter, &itemHit );
  195.         
  196.         switch( itemHit )
  197.             {
  198.             case Button_Quit:
  199.                 keepGoing = false;
  200.                 break;
  201.  
  202.             case Button_Input:
  203.                 if ( GetInputFile() )
  204.                     HiliteAControl( dialog, Button_Convert, true );
  205.                 ShowInfo( dialog );
  206.                 break;
  207.  
  208.             case Button_Convert:
  209.                 DoConversion( dialog );
  210.                 break;
  211.             
  212.             case Radio_8bit:
  213.             case Radio_16bit:            
  214.                 if ( itemHit != Group1 )
  215.                     {
  216.                     SetRadio( dialog, Group1, 0 );
  217.                     Group1 = ( Group1 == Radio_8bit ) ? Radio_16bit : Radio_8bit;
  218.                     SetRadio( dialog, Group1, 1 );
  219.                     }                
  220.                 break;
  221.  
  222.             case Radio_Mono:
  223.             case Radio_Stereo:
  224.                 if ( itemHit != Group2 )
  225.                     {
  226.                     SetRadio( dialog, Group2, 0 );
  227.                     Group2 = ( Group2 == Radio_Mono ) ? Radio_Stereo : Radio_Mono;
  228.                     SetRadio( dialog, Group2, 1 );
  229.                     }                
  230.                 break;
  231.             
  232.             case Popup_Format:
  233.             case Popup_Rate:
  234.                 break;
  235.             }
  236.         }
  237.     }
  238.  
  239. // Get a file to work on
  240.  
  241. Boolean GetInputFile()
  242.     {
  243.     StandardFileReply reply;
  244.     SFTypeList types = { 'AIFF', 'AIFC', 0, 0 };
  245.     
  246.     StandardGetFile( 0L, 2, types, &reply );
  247.     if ( reply.sfGood )
  248.         MyFileInfo.file = reply.sfFile;
  249.     
  250.     return MyFileInfo.file.name[0] != 0;
  251.     }
  252.  
  253. // Pick an output file and convert it
  254.  
  255. void DoConversion( DialogPtr dialog )
  256.     {
  257.     StandardFileReply reply;
  258.     Str63 s, t;
  259.  
  260.     BlockMove( MyFileInfo.file.name, s, MyFileInfo.file.name[0]+1 ); p2cstr(s);
  261.     sprintf( (char*)t, "%s.mod", (char*)s ); c2pstr((char*)t);
  262.     if ( t[0] > 31 )
  263.         t[0] = 31;
  264.     
  265.     StandardPutFile( "\pSave converted sound as:", t, &reply );
  266.     if ( reply.sfGood )
  267.         {
  268.         SoundConvertInfo convertInfo;
  269.         SoundConverter sc;
  270.         OSErr err;
  271.     
  272.         convertInfo.file = reply.sfFile;
  273.         GetWantedData( dialog, &convertInfo.info );
  274.                 
  275.         err = SoundConverterOpen( &MyFileInfo.info, &convertInfo.info, &sc );
  276.         if ( err )
  277.             ShowErr( "SoundConverterOpen failed", err );
  278.         else
  279.             {
  280.             if ( BuffersSize == 0 )
  281.                 {
  282.                 BuffersSize = MaxBlock();
  283.                 BuffersSize = BuffersSize * 0.4;    // try 40% of available space
  284.                 }
  285.             
  286.             err = SoundConverterGetBufferSizes( sc, BuffersSize,
  287.                             &convertInfo.inputBufferFrames,
  288.                             &convertInfo.inputBufferBytes,
  289.                             &convertInfo.outputBytes );
  290.             if ( err )
  291.                 ShowErr( "SoundConverterGetBufferSizes failed", err );
  292.             else
  293.                 ConvertSound( &MyFileInfo, &convertInfo, sc );
  294.  
  295.             SoundConverterClose(sc);
  296.             }
  297.         }
  298.     }
  299.  
  300. // Actually convert sound from input to output
  301.  
  302. OSErr ConvertSound( SoundFileInfo*input, SoundConvertInfo*output, SoundConverter sc )
  303.     {
  304.     OSErr err=0;
  305.     short inputFile=0, outputFile=0;
  306.     
  307.     // make buffers
  308.  
  309.     input->buffer = output->buffer = 0;
  310.     
  311.     input->buffer = NewPtr( output->inputBufferBytes );
  312.     err = MemError();
  313.     if ( err )
  314.         {
  315.         ShowErr( "creating input buffer", err );
  316.         goto CLEANUP;
  317.         }
  318.     output->buffer = NewPtr( output->outputBytes );
  319.     err = MemError();
  320.     if ( err )
  321.         {
  322.         ShowErr( "creating output buffer", err );
  323.         goto CLEANUP;
  324.         }
  325.     
  326.     // open files
  327.     
  328.     err = FSpOpenDF( &input->file, fsCurPerm, &inputFile );
  329.     if ( err )
  330.         {
  331.         ShowErr( "opening input file", err );
  332.         goto CLEANUP;
  333.         }
  334.         
  335.     FSpCreate ( &output->file, 'SCPL', 'AIFC', 0);    // a SoundApp creator & filetype
  336.     err = FSpOpenDF( &output->file, fsRdWrPerm, &outputFile );
  337.     if ( err )
  338.         {
  339.         ShowErr( "opening output file", err );
  340.         goto CLEANUP;
  341.         }
  342.     SetEOF( outputFile, 0 );
  343.  
  344.     SoundConverterBeginConversion( sc );
  345.     
  346.     // if it fits in memory, do it in one shot
  347.  
  348.     if ( input->numFrames <= output->inputBufferFrames )
  349.         {
  350.         err = ConvertBufferInMemory( input, inputFile, output, outputFile, sc );
  351.         if ( err )
  352.             goto CLEANUP;
  353.         }
  354.  
  355.     // otherwise convert it in chunks
  356.  
  357.     else
  358.         {
  359.         err = ConvertBufferInChunks( input, inputFile, output, outputFile, sc );
  360.         if ( err )
  361.             goto CLEANUP;
  362.         }
  363.     
  364. CLEANUP:
  365.  
  366.     if ( input->buffer )
  367.         DisposePtr( input->buffer );
  368.     if ( output->buffer )
  369.         DisposePtr( output->buffer );
  370.     if ( inputFile )
  371.         FSClose( inputFile );
  372.     if ( outputFile )
  373.         FSClose( outputFile );
  374.     FlushVol( "\p", output->file.vRefNum );
  375.     
  376.     return err;
  377.     }
  378.  
  379. // convert the input buffer when it fits in memory
  380.  
  381. OSErr ConvertBufferInMemory( SoundFileInfo*input, short inputFile,
  382.                     SoundConvertInfo*output, short outputFile, SoundConverter sc )
  383.     {
  384.     unsigned long actualOutputFrames=0;
  385.     unsigned long actualOutputSize=0;
  386.     unsigned long additionalOutputFrames=0;
  387.     unsigned long additionalOutputSize=0;
  388.     long inputFileSize;
  389.     OSErr err;
  390.  
  391.     GetEOF( inputFile, &inputFileSize );
  392.  
  393.     err = SetFPos( inputFile, fsFromStart, input->dataOffset );
  394.     if ( err )
  395.         return ShowErr( "setting position in input file", err );
  396.     inputFileSize -= input->dataOffset;
  397.  
  398.     err = FSRead( inputFile, &inputFileSize, input->buffer );
  399.     if ( err )
  400.         return ShowErr( "reading input file", err );
  401.  
  402.     err = SoundConverterConvertBuffer( sc,
  403.                         input->buffer,
  404.                         input->numFrames,
  405.                         output->buffer,
  406.                         &actualOutputFrames,
  407.                         &actualOutputSize );
  408.     if ( err )
  409.         return ShowErr( "converting sound", err );
  410.     
  411.     err = SetupAIFFHeader( outputFile,
  412.                         output->info.numChannels,
  413.                         output->info.sampleRate,
  414.                         output->info.sampleSize,
  415.                         output->info.format,
  416.                         actualOutputSize,
  417.                         actualOutputFrames );
  418.     if ( err )
  419.         return ShowErr( "setting AIFF header in file", err );
  420.     
  421.     err = FSWrite( outputFile, (long*)&actualOutputSize, output->buffer );
  422.     if ( err )
  423.         return ShowErr( "writing to output file", err );
  424.     
  425.     // end the conversion, and see if you get back a few more bytes of data
  426.     
  427.     err = SoundConverterEndConversion( sc, output->buffer,
  428.             &additionalOutputFrames, &additionalOutputSize );
  429.     if ( err )
  430.         return ShowErr( "ending conversion", err );
  431.     
  432.     // if we got a dribble more, write it to disk, then update header
  433.     
  434.     if ( additionalOutputFrames )
  435.         {
  436.         SysBeep(1);
  437.         err = FSWrite( outputFile, (long*)&additionalOutputSize, output->buffer );
  438.         if ( err )
  439.             return ShowErr( "2nd writing to output file", err );
  440.         
  441.         err = SetFPos(outputFile, fsFromStart, 0);
  442.         if ( err )
  443.             return ShowErr( "setting file position to 0", err );
  444.         
  445.         err = SetupAIFFHeader( outputFile,
  446.                             output->info.numChannels,
  447.                             output->info.sampleRate,
  448.                             output->info.sampleSize,
  449.                             output->info.format,
  450.                             actualOutputSize+additionalOutputSize,
  451.                             actualOutputFrames+additionalOutputFrames );
  452.         if ( err )
  453.             return ShowErr( "2nd setting AIFF header in file", err );
  454.         }
  455.  
  456.     return 0;
  457.     }
  458.  
  459. // convert a sound file that's too big to convert in memory at once
  460.  
  461. OSErr ConvertBufferInChunks( SoundFileInfo*input, short inputFile,
  462.                     SoundConvertInfo*output, short outputFile, SoundConverter sc )
  463.     {
  464.     OSErr err;
  465.     long inputFileSize;
  466.     long currentFrames, framesLeft=input->numFrames;
  467.     long bytesRead;
  468.     long byteWritten=0, framesWritten=0;
  469.     unsigned long actualOutputFrames=0;
  470.     unsigned long actualOutputSize=0;
  471.     unsigned long additionalOutputFrames=0;
  472.     unsigned long additionalOutputSize=0;
  473.     
  474.     // setup input file for reading
  475.  
  476.     GetEOF( inputFile, &inputFileSize );
  477.     err = SetFPos( inputFile, fsFromStart, input->dataOffset );
  478.     if ( err )
  479.         return ShowErr( "setting position in input file", err );
  480.     inputFileSize -= input->dataOffset;
  481.     
  482.     // setup dummy header ( we'll update when done )
  483.  
  484.     err = SetupAIFFHeader( outputFile,
  485.                     output->info.numChannels,
  486.                     output->info.sampleRate,
  487.                     output->info.sampleSize,
  488.                     output->info.format,
  489.                     0,
  490.                     0 );
  491.     if ( err )
  492.         return ShowErr( "setting AIFF header in file", err );
  493.  
  494.     currentFrames = output->inputBufferFrames;
  495.     
  496.     // loop through buffers of size = output->inputBufferFrames
  497.  
  498.     while ( framesLeft > 0 )
  499.         {
  500.         // read a buffer full from input file
  501.         
  502.         bytesRead = output->inputBufferBytes;
  503.         err = FSRead( inputFile, &bytesRead, input->buffer );
  504.         if ( err != 0 && err != -39 )
  505.             return ShowErr( "reading input file", err );
  506.         
  507.         // convert the buffer
  508.         
  509.         err = SoundConverterConvertBuffer( sc,
  510.                     input->buffer,
  511.                     currentFrames,
  512.                     output->buffer,
  513.                     &actualOutputFrames,
  514.                     &actualOutputSize );
  515.         if ( err )
  516.             return ShowErr( "converting sound buffer", err );
  517.         
  518.         // write the result to output file
  519.         
  520.         err = FSWrite( outputFile, (long*)&actualOutputSize, output->buffer );
  521.         if ( err )
  522.             return ShowErr( "writing sound buffer to file", err );
  523.  
  524.         byteWritten += actualOutputSize;
  525.         framesWritten += actualOutputFrames;
  526.         
  527.         // calculate remaining frames and adjust currentFrames for last buffer
  528.         
  529.         framesLeft -= currentFrames;
  530.         if ( framesLeft < output->inputBufferFrames )
  531.             currentFrames = framesLeft;
  532.         } 
  533.  
  534.     // end the conversion, and see if you get back a few more bytes of data
  535.     
  536.     err = SoundConverterEndConversion( sc, output->buffer,
  537.             &additionalOutputFrames, &additionalOutputSize );
  538.     if ( err )
  539.         return ShowErr( "ending conversion", err );
  540.     
  541.     // if we got a dribble more, write it to disk, then update header
  542.     
  543.     if ( additionalOutputFrames )
  544.         {
  545.         err = FSWrite( outputFile, (long*)&additionalOutputSize, output->buffer );
  546.         if ( err )
  547.             return ShowErr( "2nd writing to output file", err );
  548.         }
  549.         
  550.     err = SetFPos(outputFile, fsFromStart, 0);
  551.     if ( err )
  552.         return ShowErr( "setting file position to 0", err );
  553.     
  554.     err = SetupAIFFHeader( outputFile,
  555.                         output->info.numChannels,
  556.                         output->info.sampleRate,
  557.                         output->info.sampleSize,
  558.                         output->info.format,
  559.                         byteWritten+additionalOutputSize,
  560.                         framesWritten+additionalOutputFrames );
  561.     if ( err )
  562.         return ShowErr( "2nd setting AIFF header in file", err );
  563.     
  564.     return 0;
  565.     }
  566.  
  567. // Get and show info of current input file
  568.  
  569. void ShowInfo( DialogPtr dialog )
  570.     {
  571.     SetStaticText( dialog, Static_Filename, MyFileInfo.file.name );
  572.     SetStaticText( dialog, Static_Notes, "\p" );
  573.  
  574.     if ( MyFileInfo.file.name[0] != 0 )
  575.         {
  576.         short fileRef;
  577.         Str255 s;
  578.     
  579.         OSErr err = FSpOpenDF( &MyFileInfo.file, fsCurPerm, &fileRef );
  580.         if ( err )
  581.             {
  582.             ShowErr( "opening input file", err );
  583.             goto PUNT;
  584.             }
  585.     
  586.         err = ParseAIFFHeader( fileRef, &MyFileInfo.info, &MyFileInfo.numFrames, &MyFileInfo.dataOffset );
  587.         
  588.         FSClose( fileRef );
  589.  
  590.         if ( err )
  591.             {
  592.             ShowErr( "reading AIFF header", err );
  593.             goto PUNT;
  594.             }
  595.     
  596.         MakeInfoString( &MyFileInfo, s );
  597.         SetStaticText( dialog, Static_Notes, s );
  598.         return;
  599.         }
  600. PUNT:
  601.     SetStaticText( dialog, Static_Notes, "\p" );
  602.     }
  603.  
  604. // Set a SoundComponentData with current dialog settings
  605.  
  606. void GetWantedData(DialogPtr dialog, SoundComponentData* info)
  607.     {
  608.     short formatPopupIndex = GetAControlValue( dialog, Popup_Format );
  609.     short ratePopupIndex = GetAControlValue( dialog, Popup_Rate );
  610.     short isMono = GetRadio( dialog, Radio_Mono );
  611.     short is8Bit = GetRadio( dialog, Radio_8bit );
  612.     
  613.     info->flags = 0;
  614.     info->format = (*MenuToFormatTypeList)[ formatPopupIndex-1 ].formatType;
  615.     info->numChannels = isMono ? 1 : 2;
  616.     info->sampleSize = is8Bit ? 8 : 16;
  617.     info->sampleRate = MenuToSampleRates[ ratePopupIndex-1 ];
  618.     info->sampleCount = 0;
  619.     info->buffer = 0;
  620.     info->reserved = 0;
  621.     }
  622.  
  623. // build a string describing the current file
  624.  
  625. void MakeInfoString( SoundFileInfo* fileInfo, Byte* s )
  626.     {
  627.     char stereoString[16], sampleSizeString[8], sampleRateString[16], formatString[64];
  628.     double samplesPerSec;
  629.     short i, n;
  630.     Fixed f = fileInfo->info.sampleRate & 0x80000000L;
  631.     
  632.     if ( fileInfo->info.numChannels >= 2 )
  633.         strcpy( stereoString, "stereo" );
  634.     else
  635.         strcpy( stereoString, "mono" );
  636.     
  637.     sprintf( sampleSizeString, "%hd bit", fileInfo->info.sampleSize );
  638.     
  639.     samplesPerSec = Fix2X( fileInfo->info.sampleRate & 0x7fffffffL );    // unsigned Fixed value
  640.     if ( f )
  641.         samplesPerSec += 32768.0;
  642.     sprintf( sampleRateString, "%.3lf", samplesPerSec/1000.0 );
  643.     
  644.     n = GetHandleSize( (Handle) MenuToFormatTypeList ) / sizeof( MenuToFormatType );
  645.     for ( i=0; i<n; i++ )
  646.         if ( (*MenuToFormatTypeList)[i].formatType == fileInfo->info.format )
  647.             {
  648.             GetMenuItemText( GetMenuHandle( FormatPopupMenu ), i+1, (Byte*)formatString );
  649.             p2cstr( (Byte*)formatString );
  650.             break;
  651.             }
  652.     if ( i == n )
  653.         strcpy( formatString, "unknown" );
  654.     
  655.     sprintf( (char*)s, "File has %s %s sound, recorded at %s khz, with a format of “%s.”",
  656.             sampleSizeString, stereoString, sampleRateString, formatString );
  657.     c2pstr( (char*)s );
  658.     }
  659.  
  660. // Find all compressor components and add them to the format popup
  661.  
  662. void BuildFormatMenu(void)
  663.     {
  664.     Component component=0;
  665.     ComponentDescription desc, info;
  666.     Handle name = NewHandle(0);
  667.     MenuHandle menu = GetMenuHandle( FormatPopupMenu );    
  668.     short compCount=3, i;
  669.  
  670.     MenuToFormatTypeList = (MenuToFormatType**)NewHandleClear(3*sizeof(MenuToFormatType));
  671.     
  672.     // add the built in types
  673.  
  674.     (*MenuToFormatTypeList)[0].formatType = kOffsetBinary;
  675.     (*MenuToFormatTypeList)[0].isCompressor = 
  676.         (*MenuToFormatTypeList)[0].isDecompressor = true;
  677.  
  678.     (*MenuToFormatTypeList)[1].formatType = kTwosComplement;
  679.     (*MenuToFormatTypeList)[1].isCompressor = 
  680.         (*MenuToFormatTypeList)[1].isDecompressor = true;
  681.  
  682.     (*MenuToFormatTypeList)[2].formatType = 0;
  683.     
  684.     // scan through the compressors
  685.  
  686.     desc.componentType = kSoundCompressor;
  687.     desc.componentSubType = 0;
  688.     desc.componentManufacturer = 0;
  689.     desc.componentFlags = 0;
  690.     desc.componentFlagsMask = 0;
  691.     
  692.     while ( ( component = FindNextComponent( component, &desc ) ) != 0 )
  693.         {
  694.         GetComponentInfo( component, &info, name, 0, 0 );
  695.         if ( GetHandleSize(name) )
  696.             {
  697.             MenuToFormatType data;
  698.             
  699.             data.formatType = info.componentSubType;
  700.             data.isCompressor = true;
  701.             
  702.             AppendMenu( menu, "\p " );
  703.             HLock( name );
  704.             SetMenuItemText( menu, CountMItems(menu), (Byte*)*name );
  705.             HUnlock( name );
  706.             PtrAndHand( (Ptr)&data, (Handle)MenuToFormatTypeList, sizeof(MenuToFormatType) );
  707.             compCount++;
  708.             }
  709.         }
  710.     
  711.     // scan through the decompressors
  712.     
  713.     desc.componentType = kSoundDecompressor;
  714.     desc.componentSubType = 0;
  715.     desc.componentManufacturer = 0;
  716.     desc.componentFlags = 0;
  717.     desc.componentFlagsMask = 0;
  718.     
  719.     while ( ( component = FindNextComponent( component, &desc ) ) != 0 )
  720.         {
  721.         GetComponentInfo( component, &info, name, 0, 0 );
  722.         if ( GetHandleSize(name) && info.componentSubType )
  723.             {
  724.             // see if we have a compressor already, and if so, mark it
  725.  
  726.             MenuToFormatTypePtr p = *MenuToFormatTypeList;
  727.             for ( i=0; i<compCount; i++, p++ )
  728.                 if ( info.componentSubType == p->formatType )
  729.                     {
  730.                     p->isDecompressor = true;
  731.                     break;
  732.                     }
  733.             
  734.             // else create a new entry    
  735.             
  736.             if ( i == compCount )
  737.                 {
  738.                 MenuToFormatType data;
  739.                 
  740.                 data.formatType = info.componentSubType;
  741.                 data.isDecompressor = true;
  742.                 
  743.                 AppendMenu( menu, "\p " );
  744.                 HLock( name );
  745.                 SetMenuItemText( menu, CountMItems(menu), (Byte*)*name );
  746.                 HUnlock( name );
  747.                 PtrAndHand( (Ptr)&data, (Handle)MenuToFormatTypeList, sizeof(MenuToFormatType) );
  748.                 }
  749.             }
  750.         }
  751.     
  752.     DisposeHandle( name );
  753.     }
  754.  
  755. // --------- just some utility routines ---------------------------------------------------
  756.  
  757. // handle a few items in the dialog
  758.  
  759. pascal Boolean MyFilter( DialogPtr dialog, EventRecord *evt, short *itemHit )
  760.     {
  761.     WindowPtr w = (WindowPtr)(evt->message);
  762.     Boolean ans = false;
  763.  
  764.     switch(evt->what)
  765.         {
  766.         case updateEvt:
  767.             if ( w == dialog )
  768.                 {
  769.                 DoDialogUpdate(dialog);
  770.                 ans = true;
  771.                 *itemHit = 0;
  772.                 }
  773.             break;
  774.         
  775.         case mouseDown:
  776.         case mouseUp:
  777.             Where = evt->where;
  778.             GlobalToLocal(&Where);
  779.             Modifiers = evt->modifiers;
  780.             break;
  781.         }
  782.  
  783.     return ans ;
  784.     }
  785.  
  786. void DoDialogUpdate( DialogPtr dialog)
  787.     {
  788.     GrafPtr oldPort;
  789.  
  790.     GetPort(&oldPort);
  791.     SetPort(dialog);
  792.     BeginUpdate(dialog);
  793.  
  794.     UpdateDialog(dialog,dialog->visRgn);
  795.  
  796.     EndUpdate(dialog);
  797.     SetPort(oldPort);
  798.     }
  799.  
  800. void SetStaticText( DialogPtr dlog, short item, Byte* s )
  801.     {
  802.     short type;
  803.     Handle hndl;
  804.     Rect box;
  805.  
  806.     GetDialogItem(dlog,item,&type,&hndl,&box);
  807.     SetDialogItemText( hndl, s );
  808.     }
  809.  
  810. void ShowAControl( DialogPtr dlog, short item )
  811.     {
  812.     short type;
  813.     Handle hndl;
  814.     Rect box;
  815.  
  816.     GetDialogItem(dlog,item,&type,&hndl,&box);
  817.     ShowControl( (ControlHandle)hndl );
  818.     }
  819.  
  820. void HiliteAControl( DialogPtr dlog, short item, Boolean state )
  821.     {
  822.     short type;
  823.     Handle hndl;
  824.     Rect box;
  825.  
  826.     GetDialogItem(dlog,item,&type,&hndl,&box);
  827.     HiliteControl( (ControlHandle)hndl, state? 0 : 255 );
  828.     }
  829.  
  830. void SetRadio(DialogPtr dlog, short item, short val)
  831.     {
  832.     short type;
  833.     Handle hndl;
  834.     Rect box;
  835.  
  836.     GetDialogItem(dlog,item,&type,&hndl,&box);
  837.     SetControlValue((ControlHandle)hndl,val!=0);
  838.     }
  839.  
  840. short GetRadio(DialogPtr dlog, short item)
  841.     {
  842.     short type;
  843.     Handle hndl;
  844.     Rect box;
  845.     
  846.     GetDialogItem(dlog,item,&type,&hndl,&box);
  847.     return GetControlValue((ControlHandle)hndl) != 0;
  848.     }
  849.  
  850. short GetAControlValue(DialogPtr dlog, short item)
  851.     {
  852.     short type;
  853.     Handle hndl;
  854.     Rect box;
  855.     
  856.     GetDialogItem(dlog,item,&type,&hndl,&box);
  857.     return GetControlValue((ControlHandle)hndl);
  858.     }
  859.  
  860. OSErr ShowErr(char*text, short err)
  861.     {
  862.     char s[256];
  863.     if (err)
  864.         sprintf((char*)s, "Failure at %s, with error = %hd.", text, err);
  865.     else
  866.         sprintf((char*)s, "%s.", text);
  867.     c2pstr(s);
  868.     ParamText((Byte*)s, 0, 0, 0);
  869.     NoteAlert(128, 0);
  870.     return err;
  871.     }
  872.  
  873. void ShowErrAndQuit(char*text, short err)
  874.     {
  875.     ShowErr( text, err );
  876.     ExitToShell();
  877.     }
  878.